import {
  forwardRef,
  useCallback,
  useContext,
  useEffect,
  useImperativeHandle,
  useMemo,
  useState,
  useRef
} from 'react';
import ListHeader from './components/ListHeader';
import ListFloatLayerHeader from './components/ListFloatLayerHeader';
import ListContent from './components/ListContent';
import StrategyModal from './components/StrategyModal';
import { findFileIndex, getPath, isFileGone, removeVisibleFile } from '../utils/tool';
import { InBizUploadCtx, InBizUploadListCtx } from '../context';

type IProps = {
  type: string;
  tip: string;
  viewMode?: string;
};
const UploadList = forwardRef((props: IProps, ref) => {
  const {
    folderId,
    showListFloatLayer = false,
    collapsible = true,
    showAllRemoveBtn,
    fixedHeight,
    fileList,
    uploader,
    statusEnum,
    events,
    onItemClick,
    onAllRemove,
    onStatusChange,
    onUploadComplete,
    onFloatLayerClick,
    onStrategyList,
    onStrategyFileNameFormat
  } = useContext(InBizUploadCtx);
  const { viewMode } = props
  const [visibleFiles, setVisibleFiles] = useState(fileList || []);
  const [renameFiles, setRenameFiles] = useState<{ [key: string]: any }[]>([]);
  const [reNameStrategy, setReNameStrategy] = useState({ folderId: '', strategy: '' });
  const [isPackUp, setIsPackUp] = useState(false);
  const [uploadStatus, setUploadStatus] = useState('');
  const [pausable, setPausable] = useState(true);
  const folderRef = useRef('');
  const surplusNum = useMemo(() => {
    const filterStatusList = [statusEnum.UPLOAD_SUCCESSFUL, statusEnum.UPLOAD_FAILED, statusEnum.UPLOAD_EXISNAME]
    const uploadSuccessList = visibleFiles.filter(
      (item) => !filterStatusList.includes(item.status),
    );
    const uploadLoadingList = visibleFiles.filter(
      (item) => item.status === statusEnum.UPLOADING || item.status === statusEnum.UPLOAD_RETRYING,
    );
    if (uploadSuccessList.length === 0) {
      setUploadStatus('done');
    } else {
      if (uploadLoadingList.length) {
        setUploadStatus('loading');
      }
    }
    return uploadSuccessList.length;
  }, [JSON.stringify(visibleFiles)]);

  /**
   * 更新文件重名
   * @param id 文件唯一标识id
   * @param params 更新参数
   */
  const retry = (id: string | number, params: any) => {
    let file = uploader.methods.getFile(id);
    const ext = file?.name?.substring(file?.name?.lastIndexOf('.') + 1);
    file.strategy = params.strategy;
    file.fileName = params.strategy === 'rename' ? `${params.newName}.${ext}` : file.name;
    file.fileModel = params.strategy === 'rename' ? 'UPLOAD' : 'UPDATE';
    file.status = uploader.qq.status.UPLOAD_EXISNAME;
    uploader.methods.retry(id);
  };

  /**
   * 根据条件修改文件的状态
   * @param id 文件唯一标识id
   * @param status 文件状态
   * @param list 文件集合
   */
  const updateVisibleFileStatus = (id: string | number, status: string, list?: any) => {
    const currentFile = uploader.methods.getFile(id);
    const cacheVisibleFiles = list.map((item: any) => {
      return { ...item };
    });
    if (isFileGone(status, statusEnum)) {
      return removeVisibleFile(id, cacheVisibleFiles);
    }
    for (let i = 0; i < cacheVisibleFiles.length; i++) {
      if (cacheVisibleFiles[i].id === id) {
        cacheVisibleFiles[i].status = status;
        cacheVisibleFiles[i].strategy = currentFile?.strategy;
        cacheVisibleFiles[i].filename = currentFile?.name || cacheVisibleFiles[i]?.filename;
        if (status === statusEnum.UPLOAD_SUCCESSFUL) {
          const visibleFileIndex = findFileIndex(cacheVisibleFiles, id);
          if (visibleFileIndex < 0) {
            cacheVisibleFiles[i].fromServer = true;
          }
        }
        if (!cacheVisibleFiles[i].fileid) {
          cacheVisibleFiles[i] = { ...currentFile, ...cacheVisibleFiles[i] };
        }
        if (currentFile?.strategy === 'skip' && status === statusEnum.UPLOAD_FAILED) {
          let info: {
            ExistedFileId?: number | string
          } = {};
          // 上传错误的文件返回的errorInfo不是一个json格式的数据，所以需要try一下
          try {
            info = JSON.parse(currentFile?.errorInfo);
          } catch (e) {}
          cacheVisibleFiles[i].fileid = info?.ExistedFileId || '';
        }
        break;
      }
    }
    return cacheVisibleFiles;
  };

  /**
   * 文件上传状态更新事件
   * @param id 文件唯一标识id
   * @param oldStatus 文件上一次的状态
   * @param status 文件当前状态
   */
  const onFileStatusChange = (id: string | number, oldStatus: string, status: string) => {
    const currentFile = uploader.methods.getFile(id);
    const _status = currentFile.fileMd5 && status === statusEnum.CANCELED ? statusEnum.UPLOAD_SUCCESSFUL : status;
    let newFiles =
      uploader.methods.getUploads({
        status: [statusEnum.SUBMITTING, statusEnum.SUBMITTED],
      }) || [];
    if (_status == statusEnum.UPLOAD_FAILED && newFiles.length == 1 && visibleFiles[0]?.size == 0) {
      setUploadStatus('done');
    }
    if (_status === statusEnum.SUBMITTED) {
      setVisibleFiles((state) => {
        const data = [...state];
        const stateId = data.map((item) => item.id);
        const lastFile: any = newFiles.pop();
        newFiles.unshift(lastFile);
        newFiles.forEach((item) => {
          if (!stateId.includes(item.id)) {
            const { webkitRelativePath, name } = item.file;
            let path = folderRef.current;
            if (webkitRelativePath) {
              const [filePath] = webkitRelativePath.split(`/${name}`)
              path += `/${filePath}`;
            }
            data.push({ ...item, folderPath: path });
          }
        });
        return data;
      });
      if (uploadStatus !== 'loading') {
        setUploadStatus('loading');
      }
    } else if (_status === statusEnum.UPLOAD_EXISNAME) {
      if (reNameStrategy.folderId === '' || reNameStrategy.folderId !== currentFile.folderId) {
        setRenameFiles((oldDta) => {
          const index = oldDta.findIndex(item => item.id === id);
          if (index !== -1) {
            delete oldDta[index].result
          } else {
            oldDta.push({
              id,
              status,
              filename: currentFile.name,
              size: currentFile.size,
              type: currentFile.type,
            });
          }
          return oldDta;
        });
      } else {
        retry(id, reNameStrategy);
      }
    }
    if (_status !== statusEnum.SUBMITTED && _status !== statusEnum.SUBMITTING) {
      setVisibleFiles((list) => {
        const data: any = updateVisibleFileStatus(id, _status, list);
        return data;
      });
    }
    onStatusChange && onStatusChange({ id, oldStatus, status: _status });
  };

  /**
   * 单个文件上传进度的事件
   * @param id 文件唯一标识id
   * @param name 文件名称
   * @param bytesUploaded 当前文件的上传大小(进度)
   */
  const onTrackProgress = (id: string | number, name: string, bytesUploaded: string | number) => {
    setVisibleFiles((state) => {
      const files = [...state];
      const index = files.findIndex((file) => file.id === id);
      if (index !== -1) {
        files[index].filelastsize = bytesUploaded;
      }
      return files;
    });
  };

  // 全部删除按钮事件
  const onDelectAll = () => {
    Promise.resolve((onAllRemove && onAllRemove(visibleFiles)) ?? true).then((res) => {
      if (Boolean(res) === false) {
        return;
      }
      uploader.methods._handler.reset();
      uploader.methods._uploadData.reset();
      setRenameFiles([]);
      setVisibleFiles([]);
      setPausable(true);
      onAllRemove && onAllRemove();
    });
  };

  // 收起/展开按钮事件
  const onUnwindPackUp = () => {
    if (!collapsible) {
      return;
    }
    setIsPackUp(!isPackUp);
  };

  /**
   * 全部暂停/继续上传事件
   * @param type 按钮类型 pause | resum
   */
  const onResumPausedFiles = (type: string) => {
    // onOff 解决当点击全部暂停按钮时，上个文件刚好上传完成，下一个文件状态还未进入上传状态(等待状态)
    let onOff = false;
    for (let i = 0; i < visibleFiles.length; i++) {
      let fileStatus = visibleFiles[i].status;
      if (fileStatus !== statusEnum.UPLOAD_SUCCESSFUL) {
        if (type === 'pause' && fileStatus === statusEnum.UPLOADING) {
          uploader.methods.pauseUpload(visibleFiles[i]?.id, true);
          break;
        } else if (
          type === 'resum' &&
          (fileStatus === statusEnum.PAUSED || fileStatus === statusEnum.QUEUED)
        ) {
          if (!onOff && fileStatus === statusEnum.QUEUED) {
            uploader.methods.continueUpload(visibleFiles[i]?.id, { isClearFile: true, isContinueUpload: true, isAllContinueUpload: true });
            onOff = true;
          } else {
            uploader.methods.continueUpload(visibleFiles[i]?.id, { isClearFile: false, isContinueUpload: true, isAllContinueUpload: true });
          }
          break;
        }
      }
    }
    onOff = false;
    setPausable(type === 'resum' ? true : false);
    setUploadStatus(type === 'pause' ? 'pause' : 'loading');
    onFloatLayerClick && onFloatLayerClick(type);
  };

  /**
   * 文件行单击事件
   * @param fileId 文件id
   * @param status 文件状态
   */
  const onClickFileItem = (fileId: string | number, status: string) => {
    const file = visibleFiles.find((item) => item.fileid === fileId);
    if (status === statusEnum.UPLOAD_SUCCESSFUL) {
      onItemClick && onItemClick(file);
    }
  };

  /**
   * 显示重名策略弹框
   * @param id 文件id (行内点击刷新按钮时，查询对应的文件信息)
   * @param callback 回调事件
   */
  const showRenameModal = useCallback((id?: string | number, callback?: Function) => {
    let itemRename: { [key: string]: any } | undefined = undefined;
    let index = -1;
    if (id || id == 0) {
      index = renameFiles.findIndex(item => item.id == id)
    } else {
      index = renameFiles?.findIndex((item) => !item.result);
    }
    if (index === -1) {
      return null;
    }
    itemRename = renameFiles?.[index];
    const file = uploader.methods.getFile(itemRename?.id);
    if (reNameStrategy.folderId !== '' && reNameStrategy.folderId === file.folderId) {
      const cache = renameFiles.filter((item) => !item.result);
      cache.forEach((item, index) => {
        retry(item.id, reNameStrategy);
        setRenameFiles([...renameFiles].slice(index, 1));
      });
      return null;
    }
    const onClose = (result: any) => {
      callback && callback();
      if (result?.strategy) {
        retry(itemRename?.id, result);
      }
      if (result.checked) {
        setReNameStrategy({ folderId: result.folderId, strategy: result.strategy });
      }
      setRenameFiles((oldState) => {
        const renameFiles = [...oldState];
        renameFiles[index].result = result;
        renameFiles[index].filename = result?.newName || renameFiles[index].filename
        return renameFiles;
      });
    };
    return (
      itemRename && (
        <StrategyModal
          file={itemRename}
          folderId={folderId || file.folderId}
          key={itemRename.id}
          events={events}
          uploader={uploader}
          onClose={onClose}
          onStrategyList={onStrategyList}
          onStrategyFileNameFormat={onStrategyFileNameFormat}
        />
      )
    );
  }, [renameFiles, uploader]);

  // 渲染浮层列表header
  const renderListHeader = useMemo(() => {
    return (
      showAllRemoveBtn && (
        <ListHeader
          total={visibleFiles.length}
          uploadStatus={uploadStatus}
          onDelectAll={onDelectAll}
        />
      )
    );
  }, [visibleFiles.length, uploadStatus]);

  useImperativeHandle(
    ref,
    () => ({
      addFiles: async (files: { [key: string]: any }[], params: { [key: string]: any }) => {
        const res = await getPath(folderId || params.folderId);
        folderRef.current = res;
        uploader.methods.addFiles(files, params);
      },
      onComplete: (data: any) => {
        onUploadComplete && onUploadComplete(data);
      },
      getfileList: () => {
        return visibleFiles;
      },
      updateItemError: (id: string | number, error: string) => {
        visibleFiles.some((file) => {
          if (file.id === id) {
            file.error = error;
            file.status = statusEnum.UPLOAD_FAILED
            setVisibleFiles(visibleFiles);
            return true;
          }
        });
      },
      closeModal: () => {
        onDelectAll();
      },
      showPanel: () => {
        onUnwindPackUp && onUnwindPackUp();
      }
    }),
    [JSON.stringify(visibleFiles), uploader],
  );

  // 上传回调事件注册和清除
  useEffect(() => {
    uploader.on('statusChange', onFileStatusChange);
    uploader.on('progress', onTrackProgress);
    return () => {
      uploader.off('statusChange', onFileStatusChange);
      uploader.off('progress', onTrackProgress);
    };
  }, [uploader, uploadStatus, reNameStrategy]);

  // 初始文件
  useEffect(() => {
    if (fileList && fileList.length > 0) {
      uploader.methods.addInitialFiles(fileList);
    }
  }, [uploader]);

  // 文件集合中如果存在初始文件，添加到文件集合state
  useEffect(() => {
    if (fileList && fileList.length) {
      const newData = fileList.filter((file) => file?.fromServer);
      if (newData.length) {
        uploader.methods.addInitialFiles(newData);
      }
      setVisibleFiles(fileList);
    } else {
      setVisibleFiles([]);
    }
  }, [JSON.stringify(fileList)]);
  return (
    <>
      {visibleFiles.length > 0 ? (
        <div
          style={isPackUp ? { height: '60px' } : {}}
          className={showListFloatLayer ? 'react-fine-uploader-gallery-list-modal' : ''}
        >
          <InBizUploadListCtx.Provider
            value={{
              visibleFiles,
              uploadStatus,
              pausable,
              surplusNum,
              isPackUp,
              onDelectAll,
              onUnwindPackUp,
              onResumPausedFiles,
              onClickFileItem,
              showRenameModal
            }}
          >
            <div
              className={
                fixedHeight || showListFloatLayer ? 'react-fine-uploader-gallery-container' : ''
              }
              style={{ border: viewMode == 'look' ? 'none' : '', marginTop: viewMode == 'look' ? '0px' : '' }}
            >
              {viewMode != 'look' && renderListHeader}
              {showListFloatLayer && <ListFloatLayerHeader />}
              <ListContent viewMode={viewMode} />
            </div>
          </InBizUploadListCtx.Provider>
        </div>
      ) : null}
      {renameFiles.length ? showRenameModal() : null}
    </>
  );
});

export default UploadList;
